#ifndef DW_MATHS_VECTOR_INL
#	define DW_MATHS_VECTOR_INL

namespace dw {
	namespace maths {

template< typename T, unsigned N >
inline Vector< T, N >::Vector()
{
}

template< typename T, unsigned N >
inline Vector< T, N >::Vector(const T* src)
{
	dwAssert(src != NULL, "Invalid parameter: src is NULL");

	memcpy(v, src, sizeof (T) * N);
}

template< typename T, unsigned N >
inline Vector< T, N >&							Vector< T, N >::operator=(const Vector< T, N >& rhs)
{
	memcpy(v, rhs.v, sizeof (T) * N);

	return *this;
}

template< typename T, unsigned N >
inline Vector< T, N >&							Vector< T, N >::operator+=(const Vector< T, N >& rhs)
{
	for (unsigned i = 0; i < N; ++i)
	{
		v[i] += rhs.v[i];
	}

	return *this;
}

template< typename T, unsigned N >
inline Vector< T, N >&							Vector< T, N >::operator-=(const Vector< T, N >& rhs)
{
	for (unsigned i = 0; i < N; ++i)
	{
		v[i] -= rhs.v[i];
	}

	return *this;
}

template< typename T, unsigned N >
inline Vector< T, N >&							Vector< T, N >::operator*=(const T& rhs)
{
	for (unsigned i = 0; i < N; ++i)
	{
		v[i] *= rhs;
	}

	return *this;
}

template< typename T, unsigned N >
inline Vector< T, N >&							Vector< T, N >::operator/=(const T& rhs)
{
	T invRhs = 1.0f / rhs;

	for (unsigned i = 0; i < N; ++i)
	{
		v[i] *= invRhs;
	}

	return *this;
}

template< typename T, unsigned N >
inline T Vector< T, N >::length() const
{
	return static_cast< T >(::sqrtf(dot(*this, *this)));
}

template< typename T, unsigned N >
inline Vector< T, N >&							Vector< T, N >::normalize()
{
	dwAssert(length() > T(0), "");

	T temp = 1 / length();

	for (unsigned i = 0; i < N; ++i)
	{
		v[i] *= temp;
	}

	return *this;
}

template< typename T, unsigned N >
inline Vector< T, N >&							Vector< T, N >::reset()
{
	memset(v, 0, sizeof (T) * N);
	return *this;
}

template< typename T, unsigned N >
inline std::string Vector< T, N >::toString() const
{
	std::stringstream ss;
	ss << "< ";
	for (unsigned i = 0; i < N; ++i)
	{
		ss << std::setw(8) << std::setprecision(3) << v[i];
		if (i < N - 1)
			ss << ", ";
	}
	ss << " >";
	return ss.str();
}

template< typename T, unsigned N >
inline T Vector< T, N >::dot(const Vector< T, N >& v0, const Vector< T, N >& v1)
{
	T nrv(0);

	for (unsigned i = 0; i < N; ++i)
		nrv += v0[i] * v1[i];
	return nrv;
}



// Vector< T, 2 > partial specialization ////////////////////////////////////



template< typename T >
const Vector< T, 2 > Vector< T, 2 >::zero(0, 0);

template< typename T >
inline Vector< T, 2 >::Vector()
:	x(0)
,	y(0)
{
}

template< typename T >
inline Vector< T, 2 >::Vector(const T& pX, const T& pY)
:	x(pX)
,	y(pY)
{
}

template< typename T >
inline Vector< T, 2 >::Vector(const Vector< T, 2 >& ref)
:	x(ref.x)
,	y(ref.y)
{
}

template< typename T >
inline Vector< T, 2 >::Vector(const T* src)
{
	dwAssert(src != NULL, "Invalid parameter: src is NULL");

	x = src[0];
	y = src[1];
}

template< typename T >
inline Vector< T, 2 >& Vector< T, 2 >::operator=(const Vector< T, 2 >& rhs)
{
	x = rhs.x;
	y = rhs.y;

	return *this;
}

template< typename T >
inline Vector< T, 2 >& Vector< T, 2 >::operator+=(const Vector< T, 2 >& rhs)
{
	x += rhs.x;
	y += rhs.y;

	return *this;
}

template< typename T >
inline Vector< T, 2 >& Vector< T, 2 >::operator-=(const Vector< T, 2 >& rhs)
{
	x -= rhs.x;
	y -= rhs.y;

	return *this;
}

template< typename T >
inline Vector< T, 2 >& Vector< T, 2 >::operator*=(const T& rhs)
{
	x *= rhs;
	y *= rhs;

	return *this;
}

template< typename T >
inline Vector< T, 2 >& Vector< T, 2 >::operator/=(const T& rhs)
{
	x /= rhs;
	y /= rhs;

	return *this;
}

template< typename T >
inline T Vector< T, 2 >::length() const
{
	return static_cast< T >(::sqrtf(dot(*this, *this)));
}

template< typename T >
inline Vector< T, 2 >& Vector< T, 2 >::normalize()
{
	dwAssert(length() > T(0), "");

	T invLength = 1 / length();

	x *= invLength;
	y *= invLength;

	return *this;
}

template< typename T >
inline Vector< T, 2 >& Vector< T, 2 >::reset()
{
	x = 0;
	y = 0;

	return *this;
}

template< typename T >
inline std::string Vector< T, 2 >::toString() const
{
	std::stringstream ss;

	ss << "<" << x << ", " <<  y << ">";
	return ss.str();
}

template< typename T >
inline T Vector< T, 2 >::dot(const Vector< T, 2 >& v0, const Vector< T, 2 >& v1)
{
	return v0.x * v1.x + v0.y * v1.y;
}

//==============================================================================
// Vector<3, 3, T>
//==============================================================================

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

template< typename T >
/*static*/ const Vector< T, 3 > Vector< T, 3 >::zero(0, 0, 0);

template< typename T >
/*static*/ const Vector< T, 3 > Vector< T, 3 >::unitX(1, 0, 0);

template< typename T >
/*static*/ const Vector< T, 3 > Vector< T, 3 >::unitY(0, 1, 0);

template< typename T >
/*static*/ const Vector< T, 3 > Vector< T, 3 >::unitZ(0, 0, 1);

//------------------------------------------------------------------------------
// Construction & Destruction
//------------------------------------------------------------------------------

template< typename T >
inline Vector< T, 3 >::Vector()
:	x(0)
,	y(0)
,	z(0)
{
}

template< typename T >
inline Vector< T, 3 >::Vector(const T& pX, const T& pY, const T& pZ)
:	x(pX)
,	y(pY)
,	z(pZ)
{
}

template< typename T >
inline Vector< T, 3 >::Vector(const Vector< T, 3 >& ref)
:	x(ref.x)
,	y(ref.y)
,	z(ref.z)
{
}

template< typename T >
inline Vector< T, 3 >::Vector(const T* src)
{
	dwAssert(src != NULL, "Invalid parameter: src is NULL");

	x = src[0];
	y = src[1];
	z = src[2];
}

template< typename T >
inline Vector< T, 3 >::Vector(const Vector< T, 4 >& ref)
:	x(ref.x)
,	y(ref.y)
,	z(ref.z)
{
}

template< typename T >
inline Vector< T, 3 >& Vector< T, 3 >::operator=(const Vector< T, 3 >& rhs)
{
	x = rhs.x;
	y = rhs.y;
	z = rhs.z;

	return *this;
}

template< typename T >
inline Vector< T, 3 >& Vector< T, 3 >::operator+=(const Vector< T, 3 >& rhs)
{
	x += rhs.x;
	y += rhs.y;
	z += rhs.z;

	return *this;
}

template< typename T >
inline Vector< T, 3 >& Vector< T, 3 >::operator-=(const Vector< T, 3 >& rhs)
{
	x -= rhs.x;
	y -= rhs.y;
	z -= rhs.z;

	return *this;
}

template< typename T >
inline Vector< T, 3 >& Vector< T, 3 >::operator*=(const T& rhs)
{
	x *= rhs;
	y *= rhs;
	z *= rhs;

	return *this;
}

template< typename T >
inline Vector< T, 3 >& Vector< T, 3 >::operator*=(const Vector< T, 3 >& rhs)
{
	x *= rhs.x;
	y *= rhs.y;
	z *= rhs.z;

	return *this;
}

template< typename T >
inline Vector< T, 3 >& Vector< T, 3 >::operator/=(const T& rhs)
{
	T invRhs = 1.0f / rhs;

	x *= invRhs;
	y *= invRhs;
	z *= invRhs;

	return *this;
}

template< typename T >
inline T Vector< T, 3 >::sqrLength() const
{
	return static_cast< T >(dot(*this, *this));
}

template< typename T >
inline T Vector< T, 3 >::length() const
{
	return static_cast< T >(::sqrt(dot(*this, *this)));
}

template< typename T >
inline Vector< T, 3 >& Vector< T, 3 >::normalize()
{
	dwAssert(length() > T(0), "");

	T invLength = 1.0f / length();

	x *= invLength;
	y *= invLength;
	z *= invLength;

	return *this;
}

template< typename T >
inline Vector< T, 3 >& Vector< T, 3 >::reset()
{
	x = 0.0f;
	y = 0.0f;
	z = 0.0f;

	return *this;
}

template< typename T >
inline std::string Vector< T, 3 >::toString() const
{
	std::stringstream ss;

	ss << "<" << x << ", " <<  y << ", " << z << ">";

	return ss.str();
}

//------------------------------------------------------------------------------
template< typename T >
inline bool Vector< T, 3 >::isNormalized() const
{
	return std::abs(sqrLength() - 1.0f) <= 1e-6;
}

template< typename T >
inline T Vector< T, 3 >::dot(const Vector< T, 3 >& v0, const Vector< T, 3 >& v1)
{
	return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z;
}

template< typename T >
/*static*/ inline Vector< T, 3 > Vector< T, 3 >::cross(const Vector< T, 3 >& v0, const Vector< T, 3 >& v1)
{
	return Vector< T, 3 >(
		v0.y * v1.z - v0.z * v1.y,
		v0.z * v1.x - v0.x * v1.z,
		v0.x * v1.y - v0.y * v1.x);
}

template< typename T >
/*static*/ inline Vector< T, 3 > Vector< T, 3 >::min(const Vector< T, 3 >& v0, const Vector< T, 3 >& v1)
{
	return Vector< T, 3 >(
		std::min(v0.x, v1.x),
		std::min(v0.y, v1.y),
		std::min(v0.z, v1.z));
}

template< typename T >
/*static*/ inline Vector< T, 3 > Vector< T, 3 >::max(const Vector< T, 3 >& v0, const Vector< T, 3 >& v1)
{
	return Vector< T, 3 >(
		std::max(v0.x, v1.x),
		std::max(v0.y, v1.y),
		std::max(v0.z, v1.z));
}

template< typename T >
/*static*/ inline Vector< T, 3 > Vector< T, 3 >::clamp(const Vector< T, 3 >& v, const Vector< T, 3 >& min, const Vector< T, 3 >& max)
{
	dwAssert(min.x <= max.x, "Invalid parameters: min max");
	dwAssert(min.y <= max.y, "Invalid parameters: min max");
	dwAssert(min.z <= max.z, "Invalid parameters: min max");

	return Vector< T, 3 >(
		std::min(std::max(v.x, min.x), max.x),
		std::min(std::max(v.y, min.y), max.y),
		std::min(std::max(v.z, min.z), max.z));
}


//==============================================================================
// Vector<4, 4, T>
//==============================================================================

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

template< typename T >
/*static*/ const Vector< T, 4 > Vector< T, 4 >::zero(0, 0, 0, 0);

template< typename T >
/*static*/ const Vector< T, 4 > Vector< T, 4 >::unitX(1, 0, 0, 0);

template< typename T >
/*static*/ const Vector< T, 4 > Vector< T, 4 >::unitY(0, 1, 0, 0);

template< typename T >
/*static*/ const Vector< T, 4 > Vector< T, 4 >::unitZ(0, 0, 1, 0);

template< typename T >
/*static*/ const Vector< T, 4 > Vector< T, 4 >::unitW(0, 0, 0, 1);

//------------------------------------------------------------------------------
// Construction & Destruction
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >::Vector()
:	x(0)
,	y(0)
,	z(0)
,	w(0)
{
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >::Vector(const T& pX, const T& pY, const T& pZ, const T& pW)
:	x(pX)
,	y(pY)
,	z(pZ)
,	w(pW)
{
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >::Vector(const Vector< T, 4 >& ref)
:	x(ref.x)
,	y(ref.y)
,	z(ref.z)
,	w(ref.w)
{
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >::Vector(const T* src)
{
	dwAssert(src != NULL, "Invalid parameter: src is NULL");

	x = src[0];
	y = src[1];
	z = src[2];
	w = src[3]
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >::Vector(const Vector< T, 3 >& ref, const T& pW /*= 0*/)
:	x(ref.x)
,	y(ref.y)
,	z(ref.z)
,	w(pW)
{
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >::Vector(const Point< T, 3 >& ref, const T& pW /*= 1*/)
:	x(ref.x)
,	y(ref.y)
,	z(ref.z)
,	w(pW)
{
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >& Vector< T, 4 >::operator=(const Vector< T, 4 >& rhs)
{
	x = rhs.x;
	y = rhs.y;
	z = rhs.z;
	w = rhs.w;

	return *this;
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >& Vector< T, 4 >::operator+=(const Vector< T, 4 >& rhs)
{
	x += rhs.x;
	y += rhs.y;
	z += rhs.z;
	w += rhs.w;

	return *this;
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >& Vector< T, 4 >::operator-=(const Vector< T, 4 >& rhs)
{
	x -= rhs.x;
	y -= rhs.y;
	z -= rhs.z;
	w -= rhs.w;

	return *this;
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >& Vector< T, 4 >::operator*=(const T& rhs)
{
	x *= rhs;
	y *= rhs;
	z *= rhs;
	w *= rhs;

	return *this;
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >& Vector< T, 4 >::operator/=(const T& rhs)
{
	T invRhs = 1.0f / rhs;

	x *= invRhs;
	y *= invRhs;
	z *= invRhs;
	w *= invRhs;

	return *this;
}

template< typename T >
inline T Vector< T, 4 >::sqrLength() const
{
	return static_cast< T >(dot(*this, *this));
}

//------------------------------------------------------------------------------
template< typename T >
inline T Vector< T, 4 >::length() const
{
	return static_cast< T >(::sqrtf(dot(*this, *this)));
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >& Vector< T, 4 >::normalize()
{
	T invLength = 1.0f / length();

	x *= invLength;
	y *= invLength;
	z *= invLength;
	w *= invLength;

	return *this;
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >& Vector< T, 4 >::reset()
{
	x = 0;
	y = 0;
	z = 0;
	w = 0;

	return *this;
}

//------------------------------------------------------------------------------
template< typename T >
inline Vector< T, 4 >& Vector< T, 4 >::set(const T& pX, const T& pY, const T& pZ, const T& pW)
{
	x = pX;
	y = pY;
	z = pZ;
	w = pW;

	return *this;
}

//------------------------------------------------------------------------------
template< typename T >
inline std::string Vector< T, 4 >::toString() const
{
	std::stringstream ss;

	ss << "<" << x << ", " <<  y << ", " << z << ", " << w << ">";

	return ss.str();
}

//------------------------------------------------------------------------------
template< typename T >
inline bool Vector< T, 4 >::isNormalized() const
{
	return (1.0f - sqrLength()) <= std::numeric_limits< T >::epsilon();
}

//------------------------------------------------------------------------------
template< typename T >
inline T Vector< T, 4 >::dot(const Vector< T, 4 >& v0, const Vector< T, 4 >& v1)
{
	return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z + v0.w * v1.w;;
}

// friends ////////////////////////////////////////////////////////////////


template< typename T >
inline bool operator==(const Vector< T, 3 >& lhs, const Vector< T, 3 >& rhs)
{
	return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
}


template< typename T, unsigned N >
inline bool operator==(const Vector< T, N >& lhs, const Vector< T, N >& rhs)
{
	return memcmp(&lhs[0], &rhs[0], sizeof (T) * N) == 0;
}

template< typename T, unsigned N >
inline bool operator!=(const Vector< T, N >& lhs, const Vector< T, N >& rhs)
{
	return memcmp(&lhs[0], &rhs[0], sizeof (T) * N) != 0;
}

template< typename T, unsigned N >
inline Vector< T, N >								operator+(const Vector< T, N >& lhs, const Vector< T, N >& rhs)
{
	return Vector< T, N >(lhs) += rhs;
}

template< typename T >
inline Vector< T, 3 >								operator + (const Vector< T, 3 >& lhs, const Vector< T, 3 >& rhs)
{
	return Vector< T, 3 >(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
}

template< typename T, unsigned N >
inline Vector< T, N >								operator-(const Vector< T, N >& rhs)
{
	Vector< T, N > nrv;
	nrv.reset();
	nrv -= rhs;
	return nrv;
}

template< typename T, unsigned N >
inline Vector< T, N >								operator-(const Vector< T, N >& lhs, const Vector< T, N >& rhs)
{
	Vector< T, N > nrv(lhs);
	nrv -= rhs;
	return nrv;
}

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const Vector< T, N >& lhs, const T& rhs)
{
	Vector< T, N > nrv(lhs);
	nrv *= rhs;
	return nrv;
}

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const Vector< T, N >& lhs, const Vector< T, N >& rhs)
{
	Vector< T, N > nrv(lhs);
	nrv *= rhs;
	return nrv;
}

template< typename T >
inline Vector< T, 3 >								operator*(const Vector< T, 3 >& lhs, const Vector< T, 3 >& rhs)
{
	return Vector< T, 3 >(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z);
}

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const T& lhs, const Vector< T, N >& rhs)
{
	Vector< T, N > nrv(rhs);
	nrv *= lhs;
	return nrv;
}

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const Vector< T, N >& lhs, const Matrix< T, N, N >& rhs)
{
	Vector< T, N > nrv;

	for (unsigned j = 0; j < N; ++j)
	{
		T tmp = 0;
		for (unsigned i = 0; i < N; ++i)
			tmp += rhs(i, j) * lhs[i];
		nrv[j] = tmp;
	}
	return nrv;
}

template< typename T >
inline Vector< T, 4 >								operator*(const Matrix< T, 4, 4 >& lhs, const Vector< T, 4 >& rhs)
{
	return Vector< T, 4 >(lhs.right.x * rhs.x + lhs.up.x * rhs.y + lhs.at.x * rhs.z + lhs.pos.x * rhs.w,
		                lhs.right.y * rhs.x + lhs.up.y * rhs.y + lhs.at.y * rhs.z + lhs.pos.y * rhs.w,
                        lhs.right.z * rhs.x + lhs.up.z * rhs.y + lhs.at.z * rhs.z + lhs.pos.z * rhs.w,
                        lhs.right.w * rhs.x + lhs.up.w * rhs.y + lhs.at.w * rhs.z + lhs.pos.w * rhs.w );
}

template< typename T, unsigned N >
inline Vector< T, N >								operator*(const Matrix< T, N, N >& lhs, const Vector< T, N >& rhs)
{
	Vector< T, N > nrv;

	for (unsigned j = 0; j < N; ++j)
	{
		T tmp = 0;
		for (unsigned i = 0; i < N; ++i)
			tmp += lhs(i, j) * rhs[i];
		nrv[j] = tmp;
	}
	return nrv;
}

template< typename T, unsigned N >
inline Vector< T, N >								operator/(const Vector< T, N >& lhs, const T& rhs)
{		  
	Vector< T, N > nrv(lhs);
	nrv /= rhs;
	return nrv;
}

template< typename T, unsigned N >
inline std::ostream& operator<< (std::ostream& os, const Vector< T, N >& rhs)
{
	os << "< ";
	for (unsigned i = 0; i < N; ++i)
	{
		os << std::setw(8) << std::setprecision(3) << rhs[i];
		if (i < N - 1)
			os << ", ";
	}
	os << " >";
	return os;
}

	} // namespace maths
} // namespace dw

#endif // !DW_MATHS_VECTOR_INL